<?php

/**
 * Beaver_Validate
 * 
 * @category   Beaver
 * @package    Beaver
 * @uses       Zend_Validate
 * @author     Mihály Kuprivecz
 */
class Beaver_Validate extends Zend_Validate
{
	const ATTRIBUTE_TYPE_BOOL   = "boolean";
	const ATTRIBUTE_TYPE_INT    = "integer";
	const ATTRIBUTE_TYPE_STRING = "string";
	const ATTRIBUTE_TYPE_ARRAY  = "array";
	const ATTRIBUTE_TYPE_OBJECT = "object";
	
	/**
	 * Default Zend_Validator classes and it's parameters
	 *
	 * @var array
	 */
	protected $_validatorDefaults = array(
	
		"alnum"        => array( 
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_BOOL),
			"name"     => array("allowWhiteSpace"),
			"required" => array(false),
			"constructor" => true
		),
		"alpha"        => array( 
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_BOOL),
			"name"     => array("allowWhiteSpace"),
			"required" => array(false),
			"constructor" => true
		),
		"barcode"      => array( 
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_STRING),
			"name"     => array("type"),
			"required" => array(true),
			"constructor" => true
		),
		"between"      => array( 
			"type"     => array(
				Beaver_Validate::ATTRIBUTE_TYPE_INT, 
				Beaver_Validate::ATTRIBUTE_TYPE_INT,
				Beaver_Validate::ATTRIBUTE_TYPE_BOOL
			),
			"name"     => array("min","max","inclusive"),
			"required" => array(true, true, false),
			"constructor" => true
		),
		"ccnum"        => array(),
		"date"         => array( 
			"type"     => array(
				Beaver_Validate::ATTRIBUTE_TYPE_INT,
				Beaver_Validate::ATTRIBUTE_TYPE_STRING
			),
			"name"     => array("format","locale"),
			"required" => array(false, false),
			"constructor" => true
		),
		"digits"       => array(),
		"emailAddress" => array(
			"type"     => array(
				Beaver_Validate::ATTRIBUTE_TYPE_INT,
				Beaver_Validate::ATTRIBUTE_TYPE_BOOL,
				Beaver_Validate::ATTRIBUTE_TYPE_OBJECT
			),
			"name"     => array("allow", "validateMx", "hostnameValidator"),
			"required" => array(false, false, false),
			"constructor" => true
		),
		"float"        => array(
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_STRING),
			"name"     => array("locale"),
			"required" => array(false),
			"constructor" => true
		),
		"greaterThan"  => array(
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_INT),
			"name"     => array("min"),
			"required" => array(true),
			"constructor" => false
		),
		"hex"          => array(),
		"hostname"     => array(
			"type"     => array(
				Beaver_Validate::ATTRIBUTE_TYPE_INT,
				Beaver_Validate::ATTRIBUTE_TYPE_BOOL,
				Beaver_Validate::ATTRIBUTE_TYPE_BOOL,
				Beaver_Validate::ATTRIBUTE_TYPE_OBJECT
			),
			"name"     => array("allow", "validateIdn", "validateTld", "ipValidator"),
			"required" => array(false, false, false, false),
			"constructor" => true
		),
		"iban"         => array(
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_STRING),
			"name"     => array("locale"),
			"required" => array(false),
			"constructor" => true
		),
		"identical"    => array(
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_STRING),
			"name"     => array("token"),
			"required" => array(false),
			"constructor" => true
		),
		"inArray"      => array(
			"type"     => array(
				Beaver_Validate::ATTRIBUTE_TYPE_ARRAY,
				Beaver_Validate::ATTRIBUTE_TYPE_BOOL
			),
			"name"     => array("haystack", "strict"),
			"required" => array(true,false),
			"constructor" => true
		),
		"int"          => array(
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_STRING),
			"name"     => array("locale"),
			"required" => array(false),
			"constructor" => true
		),
		"ip"           => array(),
		"lessThan"     => array(
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_INT),
			"name"     => array("min"),
			"required" => array(true),
			"constructor" => false
		),
		"notEmpty"     => array(),
		"regex"        => array(
			"type"     => array(Beaver_Validate::ATTRIBUTE_TYPE_STRING),
			"name"     => array("pattern"),
			"required" => array(true),
			"constructor" => true
		),
		"stringLength" => array(
			"type"     => array(
				Beaver_Validate::ATTRIBUTE_TYPE_INT,
				Beaver_Validate::ATTRIBUTE_TYPE_INT,
				Beaver_Validate::ATTRIBUTE_TYPE_STRING
			),
			"name"     => array("min", "max", "encoding"),
			"required" => array(false,true,false),
			"constructor" => true
		)
	);
	
	public function addValidator(Zend_Validate_Interface $validator, $message = null, $parameters = array())
    {
        $this->_validators[] = array(
            'instance' => $validator,
            'breakChainOnFailure' => false,
        	'parameters' => $parameters,
        	'message' => $message
            );
        return $this;
    }
	
	/**
	 * Create a new validation criteria to the validation object
	 *
	 * @param string $type
	 * @param array $parameters
	 */
	public function addValidatorWithParameters($type, array $parameters, $message = null)
	{
		if (!isset($this->_validatorDefaults[$type]))
		{
			throw new Beaver_Validate_Exception("Not valid validator type '" . $type . "'");
		}
		$validatorClass = "Zend_Validate_" . ucfirst($type);

		if ( $this->_validatorDefaults[$type]["constructor"] )
		{
			$arguments = array();
			for($i=0; $i < count($this->_validatorDefaults[$type]["name"]); $i++)
			{
				$name = $this->_validatorDefaults[$type]["name"][$i];
				
				if ( array_key_exists($name, $parameters))
				{
					if ( gettype($parameters[$name]) != $this->_validatorDefaults[$type]["type"][$i] && $this->_validatorDefaults[$type]["required"][$i])
					{
						throw new Beaver_Validate_Exception("Not valid type in value of attribute '" . $name . "' in validator '" . $type . "', the type got '" . gettype($value) . "' instead of '" . $this->_validatorDefaults[$type]["type"][$i] . "'");
					}
					$arguments[$i] = $parameters[$name];
				}
				else
				{
					$arguments[$i] = null;
				}
			}
			$reflectionObj = new ReflectionClass($validatorClass); 
			$validator     = $reflectionObj->newInstanceArgs($arguments);
		}
		else
		{
			$validator = $validatorClass();
			foreach ($parameters["attributes"] as $name => $value) {
				if(!isset($this->_validatorDefaults[$type][$name]))
				{
					throw new Beaver_Validate_Exception("Not valid attribute '" . $name . "' in validator '" . $type . "'");
				}
				if ( gettype($value) != $this->_validatorDefaults[$type][$name])
				{
					throw new Beaver_Validate_Exception("Not valid type in value of attribute '" . $name . "' in validator '" . $type . "', the type got '" . gettype($value) . "' instead of '" . $this->_validatorDefaults[$type][$name] . "'");
				}
				call_user_func_array(array($validator, "set" . ucfirst($name)), array($value));
			}
		}
		$this->addValidator($validator, $message, $parameters );
	}
	
	/**
	 * Returns true if and only if $value passes all validations in the chain
	 *
	 * Validators are run in the order in which they were added to the chain (FIFO).
	 *
	 * @param  mixed $value
	 * @return boolean
	 */
	public function isValid($value) {
		$this->_messages = array();
		$this->_errors   = array();
		$result = true;
		foreach ($this->_validators as $element) {
			$validator = $element['instance'];
			if ($validator->isValid($value)) {
				continue;
			}
			$result = false;
			$messages = $validator->getMessages();
			$keys = array_keys($messages);
			if (null !== $element['message'])
			{
				$messages = array();
				$messages[$keys[0]] = Beaver_Language::getInstance()->get("Validator", $element['message'], $element['parameters']);
			}
			$this->_messages = array_merge($this->_messages, $messages);
			$this->_errors   = array_merge($this->_errors,   array_keys($messages));
			if ($element['breakChainOnFailure']) {
				break;
			}
		}
		return $result;
	}
}